package com.umbacos.udm.client.presenter;

import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.Response;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.HTMLTable.Cell;
import com.umbacos.udm.client.arch.ContextMenuList;
import com.umbacos.udm.client.arch.HourGlass;
import com.umbacos.udm.client.arch.ImageButton;
import com.umbacos.udm.client.arch.MimeType;
import com.umbacos.udm.client.arch.Popup;
import com.umbacos.udm.client.arch.Table;
import com.umbacos.udm.client.arch.Popup.uPopupHandler;
import com.umbacos.udm.client.model.AsyncComm;
import com.umbacos.udm.client.model.File;
import com.umbacos.udm.client.model.Files;
import com.umbacos.udm.client.model.Presenter;
import com.umbacos.udm.client.model.UdmStatus;
import com.umbacos.udm.client.model.View;
import com.umbacos.udm.client.model.File.FileType;
import com.umbacos.udm.client.model.Files.SortOrder;
import com.umbacos.udm.client.model.UdmStatus.UdmSubscriber;
import com.umbacos.udm.client.presenter.popup.TorrentPopupPresenter;
import com.umbacos.udm.client.presenter.popup.UploadPopupPresenter;
import com.umbacos.udm.client.view.FileListView;

public class FileListPresenter implements Presenter, ClickHandler, Table.EventHandler, UdmSubscriber {

	//* Application status holder
	private UdmStatus udmStatus;
	//* Class view of status holder
	private Files myFileList = new Files();

	private Table ft;
	private Grid fh;
	private Label curDir;
	private HourGlass hourglass;

	/**
	 * Contextmenu callbacks constants
	 */
	private final static int CM_PLAY=0;
	private final static int CM_META=1;
	private final static int CM_COPY=2;
	private final static int CM_MOVE=3;
	private final static int CM_RENAME=4;
	private final static int CM_STUFF=5;
	private final static int CM_DELETE=6;

	private static enum treeOperations {CHDIR, MOVE, COPY}

	private int refreshInterval = 1000;
	private Timer refreshTimer;
	
	/**
	 * Bind to objects in the Mainview
	 */
	public void bind(final View mv, final UdmStatus us) {

		udmStatus = us;
		//* Get alerted when files are updated:
		udmStatus.addFileSubscription(this);

		hourglass = new HourGlass(null);
		hourglass.wait(true);

		final FileListView fv = new FileListView();
		mv.add(fv);
		
		//* File Table
		ft = fv.getFtFiles();
		ft.addEventHandler(this);
		ft.getColumnFormatter().setWidth(3, ft.getParent().getOffsetWidth()-265 + "px");

		//* File Table Header
		fh = fv.getHeader();
		fh.addClickHandler(this);

		//* Icon Menu
		fv.ibNew.addClickHandler(new HandlerNewDir());
		fv.ibRefr.addClickHandler(new HandlerRefresh());
		fv.ibTree.addClickHandler(new HandlerTree());
		fv.ibUpl.addClickHandler(new HandlerUpl());
		fv.ibDisk.addClickHandler(new ClickHandler() {
			
			@Override
			public void onClick(ClickEvent event) {
				hourglass.center();
				AsyncComm ac=new AsyncComm();
				ac.addPostData("cmd", "disk");
				ac.send(AsyncComm.COMM, new RequestCallback() {
					
					@Override
					public void onResponseReceived(Request request, Response response) {
						Popup pp=new Popup("Disk utilization", new HTML("<pre>" + response.getText() + "</pre>"), Popup.POPUP_CLOSE);
						pp.showPopup();
						hourglass.hide();
					}
					
					@Override
					public void onError(Request request, Throwable exception) {
						Popup pp = new Popup("Server Error", AsyncComm.SERVER_ERROR, Popup.POPUP_CLOSE);
						pp.showPopup();						
					}
				});
			}
		});

		//* Path Label
		curDir=fv.curDir;		

		hourglass.wait(false);

		// refresh interval listbox
		refreshInterval=Integer.parseInt(fv.lbFreq.getValue(fv.lbFreq.getSelectedIndex()));
		fv.lbFreq.addChangeHandler(new ChangeHandler() {
			
			@Override
			public void onChange(ChangeEvent event) {
				refreshInterval=Integer.parseInt(fv.lbFreq.getValue(fv.lbFreq.getSelectedIndex()));
				refreshTimer.cancel();
				if (refreshInterval>0) refreshTimer.schedule(1);
			}
		});
		
		// setup timer to refresh list automatically
		refreshTimer = new Timer() {
			@Override
			public void run()
			{
				udmStatus.update(UdmStatus.UPDATE_FILES);
			}
		};
}

	/**
	 * Populate table with file data (called by the subscription update()
	 */
	public void refresh() {
		hourglass.wait(true);
		ft.resize(myFileList.size(), 4);
		int i=0; Image icon;
		for (; i<myFileList.size();++i){
			File f = myFileList.get(i);
			if (f.inode.length()>0)
				icon = new Image(decodeMime(f));
			else
				icon = new Image("images/directory_up.png");
			icon.setStyleName("menuicon");
			ft.setText(i, 0, f.date);
			ft.setText(i, 1, f.size);
			ft.setWidget(i, 2, icon);
			ft.setText(i, 3, f.file);
			ft.getCellFormatter().setAlignment(i, 0, HasHorizontalAlignment.ALIGN_RIGHT,HasVerticalAlignment.ALIGN_MIDDLE);
			ft.getCellFormatter().setAlignment(i, 1, HasHorizontalAlignment.ALIGN_RIGHT,HasVerticalAlignment.ALIGN_MIDDLE);
			ft.getCellFormatter().setAlignment(i, 2, HasHorizontalAlignment.ALIGN_CENTER,HasVerticalAlignment.ALIGN_MIDDLE);
			ft.getCellFormatter().setAlignment(i, 3, HasHorizontalAlignment.ALIGN_LEFT,HasVerticalAlignment.ALIGN_MIDDLE);
			ft.getCellFormatter().setStyleName(i, 3, "underlined");
		}
		hourglass.wait(false);
	}

	private String decodeMime(File f){

		if (f.type == FileType.FILE) {
			String ext = f.file.substring(f.file.lastIndexOf('.')+1);
			if (MimeType.decode.containsKey(ext)) 
				return MimeType.decode.get(ext);
			else
				return MimeType.decode.get("or_else");
		} else {
			return "images/directory.png";
		}

	}

	/**
	 * Column Sort
	 */
	@Override
	public void onClick(ClickEvent event) {

		Cell cell = fh.getCellForEvent(event);
		if (cell != null) {
			hourglass.wait(true);
			switch (cell.getCellIndex()) {
			case 0: 
				if (myFileList.currentSort == SortOrder.DATE) myFileList.toggleSort();
				myFileList.sortFiles(SortOrder.DATE); 
				break;
			case 1:
				if (myFileList.currentSort == SortOrder.SIZE) myFileList.toggleSort();
				myFileList.sortFiles(SortOrder.SIZE);
				break;
			case 3: 
				if (myFileList.currentSort == SortOrder.NAME) myFileList.toggleSort();
				myFileList.sortFiles(SortOrder.NAME); 
				break;				
			}

			arrowOnFh();
			this.refresh();
			hourglass.wait(false);
		}
	}

	private void arrowOnFh(){
		String icon;
		if (myFileList.isSortAsc())
			icon = "images/sortdn.png";
		else
			icon = "images/sortup.png";

		fh.setHTML(0,0, "Date");
		fh.setHTML(0,1, "Size");
		fh.setHTML(0,3, "Name");				
		switch (myFileList.currentSort) {
		case DATE: fh.setHTML(0,0, "<img src='" + icon + "' align=absmiddle> Date"); break;
		case SIZE: fh.setHTML(0,1, "<img src='" + icon + "' align=absmiddle> Size"); break;
		case NAME: fh.setHTML(0,3, "Name <img src='" + icon + "' align=absmiddle>"); break;				
		}
	}

	/**
	 * Click to download a file or to change directory
	 */
	@Override
	public void clickCallback(int rowIndex, int cellIndex) {
		if (cellIndex != 3) return;
		switch (myFileList.get(rowIndex).type) {
		case DIR:
			hourglass.wait(true);
			udmStatus.setCurDir(myFileList.get(rowIndex).file);
			break;
		case FILE:
			String inode = myFileList.get(rowIndex).inode;
			String clickUrl = AsyncComm.getBaseUrl() + AsyncComm.DOWN + "?file=" + inode + "&curdir=" + udmStatus.getCurDir();
			Window.open(clickUrl, "Download", "");		
			break;
		}
	}

	/**
	 * Contextmenu callbacks
	 */
	@Override
	public void buildContextMenuCallback(ContextMenuList popup) {

		File f = myFileList.get(popup.getRowIndex());
		if (f.file.endsWith(".torrent")) {
			popup.add(CM_PLAY, new ImageButton("images/play.png", 100, 16, 29), new Label("Start the Download"));
		}
		popup.add(CM_COPY, new ImageButton("images/moveto.png", 100, 16, 29), new Label("Copy to another folder"));
		popup.add(CM_MOVE, new ImageButton("images/moveto.png", 100, 16, 29), new Label("Move to another folder"));
		popup.add(CM_RENAME, new ImageButton("images/rename.png", 100, 16, 29), new Label("Rename"));
		popup.add(CM_STUFF, new ImageButton("images/stuff.png", 100, 16, 29), new Label("Compress to tar.gz"));
		popup.add(CM_DELETE, new ImageButton("images/delete.png", 100, 16, 29), new Label("Delete"));
		if (f.file.endsWith(".torrent")) {
			popup.add(CM_META, new ImageButton("images/lens.png", 100, 16, 29), new Label("Properties"));
		}
	}

	/**
	 * Context Menu handlers
	 */
	@Override
	public void contextMenuClickCallback(final int command, final int rowIndex, final int cellIndex) {
		switch (command){
		case CM_PLAY:
		case CM_META:
			hourglass.wait(true);
			TorrentPopupPresenter tp=new TorrentPopupPresenter(udmStatus);
			tp.loadAndBind(command, udmStatus.getCurDir() + myFileList.get(rowIndex).file);
			hourglass.wait(false);
			break;
		case CM_RENAME:
			final TextBox tb= new TextBox();
			tb.setText(myFileList.get(rowIndex).file);
			Popup pp0=new Popup("Rename", tb, Popup.POPUP_OK + Popup.POPUP_CANCEL);
			pp0.setAction(hourglass, "rename", udmStatus.getCurDir() + myFileList.get(rowIndex).file, udmStatus.getCurDir() + (tb.getText()), udmStatus);
			pp0.showPopup();
			break;
		case CM_COPY:
			handleTreeOperations(treeOperations.COPY, udmStatus.getCurDir() + myFileList.get(rowIndex).file);
			break;
		case CM_MOVE:
			handleTreeOperations(treeOperations.MOVE, udmStatus.getCurDir() + myFileList.get(rowIndex).file);
			break;
		case CM_STUFF:
			Popup pp1=new Popup("Create a tar.gz archive of '" + myFileList.get(rowIndex).file + "'", new Label(), Popup.POPUP_OK + Popup.POPUP_CANCEL);
			pp1.setAction(hourglass, "targz", udmStatus.getCurDir() + myFileList.get(rowIndex).file, "", udmStatus);
			pp1.showPopup();
			break;
		case CM_DELETE:
			Popup pp2=new Popup("Delete '" + myFileList.get(rowIndex).file + "'?", new Label(), Popup.POPUP_OK + Popup.POPUP_CANCEL);
			pp2.setAction(hourglass, "rem", udmStatus.getCurDir() + myFileList.get(rowIndex).file, "", udmStatus);
			pp2.showPopup();
			break;
		}		
	}		

	/**
	 * Subscription calllback
	 */
	@Override
	public void update() {
		curDir.setText("Current Directory: " + udmStatus.getCurDir());
		Files dnl = udmStatus.dnl;
		myFileList.clear();
		if (udmStatus.getCurDir().length()>1)
			myFileList.add(new File("..", FileType.DIR, "", "4 K", "8/1/1970 5:35", false));
		for (int i=0; i<dnl.size(); ++i) myFileList.add(dnl.get(i));
		myFileList.sortFiles(myFileList.currentSort);
		if (refreshInterval>0) refreshTimer.schedule(refreshInterval);
		refresh();
	}

	/**
	 * Clicked on the Make New Dir menu icon
	 */
	class HandlerNewDir implements ClickHandler {
		public void onClick(ClickEvent event) {
			Popup pp=new Popup("Create a new Directory", new TextBox(), Popup.POPUP_OK + Popup.POPUP_CANCEL);
			pp.setAction(hourglass, "mkdir", udmStatus.getCurDir() + ((TextBox)pp.dialogVPanel.getWidget(0)).getText(), "", udmStatus);
			pp.showPopup();
		}
	}

	/**
	 * Clicked on the Refresh menu icon
	 */
	class HandlerRefresh implements ClickHandler {
		public void onClick(ClickEvent event) {
			hourglass.wait(true);
			udmStatus.update(UdmStatus.UPDATE_FILES);
		}
	}

	/**
	 * Clicked on the Tree menu icon
	 */
	class HandlerUpl implements ClickHandler {
		public void onClick(ClickEvent event) {
			UploadPopupPresenter up=new UploadPopupPresenter(udmStatus);
			up.bind();
		}
	}

	/**
	 * Clicked on the Tree menu icon
	 */
	class HandlerTree implements ClickHandler {
		public void onClick(ClickEvent event) {
			hourglass.center();
			handleTreeOperations(treeOperations.CHDIR, "");
		}
	}

	private void handleTreeOperations(final treeOperations what, final String file) {

		AsyncComm ac=new AsyncComm();
		ac.addPostData("cmd", "tree");
		ac.addPostData("file", "treepop");
		ac.send(AsyncComm.STAT, new RequestCallback() {

			@Override
			public void onResponseReceived(Request request, Response response) {
				if (200 == response.getStatusCode()) {          
					ScrollPanel sp = new ScrollPanel();
					sp.add(new HTML(response.getText()));
					sp.setSize("600px", "400px");
					String caption;
					if (file.length()>0)
						caption = " for '" + file + "':";
					else
						caption = ":";
					Popup pp = new Popup("Select the destination Directory" + caption, sp, Popup.POPUP_OK + Popup.POPUP_CANCEL);
					pp.addEventHandler(new uPopupHandler() {							
						@Override
						public void okClickCallback(Popup pp) {
							switch (what) {
							case CHDIR:
								udmStatus.setCurDir(getTree("treepop"));
								break;
							case MOVE:
								udmStatus.standardRequest("rename", file, getTree("treepop"));
								break;
							case COPY:
								udmStatus.standardRequest("copy", file, getTree("treepop") + "/");
								break;
							}
							pp.hide();
						}

						native String getTree(String t)/*-{
							return $wnd.getTree(t);
						}-*/;
					});
					pp.showPopup();
					uTreeInit("treepop");
					hourglass.hide();
				} 
				else {        
					Popup pp = new Popup("Server Error", new HTML(AsyncComm.SERVER_ERROR + 
							"<br>" + response.getStatusCode() + 
							" " + response.getStatusText()),
							Popup.POPUP_CLOSE);
					pp.showPopup();
				} 
			}

			native void uTreeInit(String t)/*-{
				$wnd.initTree(t);
			}-*/;

			@Override
			public void onError(Request request, Throwable exception) {
				Popup pp = new Popup("Server Error", AsyncComm.SERVER_ERROR, Popup.POPUP_CLOSE);
				pp.showPopup();
			}
		});

	}
}
